from __future__ import annotations

import datetime
from typing import Any, Dict, List, Optional

from backend.database.db import db_manager
from backend.logger_setup import get_logger

from .domain import Message, Role, Session
from .exceptions import RepositoryError

logger = get_logger("chat.repository")


class SessionRepository:
    """Data access layer for chat sessions."""

    def __init__(self):
        self._db = db_manager

    def get(self, session_id: str) -> Optional[Session]:
        """Fetch a session by id."""
        try:
            data = self._db.get_session_by_id(session_id)
        except Exception as exc:  # pragma: no cover - defensive fallback
            logger.error(f"加载会话失败: {exc}")
            raise RepositoryError("加载会话失败", details=str(exc)) from exc

        if not data:
            return None

        # db_manager already returns dict-like rows
        return Session(
            id=str(data.get("id") or session_id),
            user_id=data.get("user_id"),
            title=data.get("title"),
            created_at=data.get("created_at"),
            updated_at=data.get("updated_at"),
        )

    def create(self, session_id: str, user_id: str, title: str, now: datetime.datetime):
        """Create a session row with DB-specific placeholders."""
        try:
            with self._db.get_cursor() as cursor:
                if self._db.db_type == "sqlite":
                    cursor.execute(
                        """
                        INSERT INTO sessions (id, user_id, title, created_at, updated_at)
                        VALUES (?, ?, ?, ?, ?)
                        """,
                        (session_id, user_id, title, now, now),
                    )
                else:
                    cursor.execute(
                        """
                        INSERT INTO sessions (id, user_id, title, created_at, updated_at)
                        VALUES (%s, %s, %s, %s, %s)
                        """,
                        (session_id, user_id, title, now, now),
                    )
        except Exception as exc:  # pragma: no cover - DB failure fallback
            self.rollback()
            logger.error(f"创建会话失败: {exc}")
            raise RepositoryError("创建会话失败", details=str(exc)) from exc

    def assign_messages_user(self, session_id: str, user_id: str):
        """Assign orphan messages to a user for the session."""
        try:
            return self._db.assign_messages_user(session_id, user_id)
        except Exception as exc:  # pragma: no cover - DB failure fallback
            logger.error(f"更新消息归属失败: {exc}")
            raise RepositoryError("更新消息归属失败", details=str(exc)) from exc

    def rollback(self):
        """Rollback current DB transaction if possible."""
        try:
            conn = getattr(self._db, "connection", None)
            if conn:
                conn.rollback()
        except Exception as exc:  # pragma: no cover - defensive
            logger.warning(f"会话回滚失败: {exc}")


class MessageRepository:
    """Data access layer for chat messages."""

    def __init__(self):
        self._db = db_manager

    def add_message(
        self,
        session_id: str,
        role: str,
        content: str,
        metadata: Optional[Dict[str, Any]],
        user_id: Optional[str] = None,
    ) -> Optional[str]:
        """Persist a chat message."""
        try:
            return self._db.insert_session_message(
                session_id=session_id,
                role=role,
                content=content,
                metadata=metadata,
                user_id=user_id,
            )
        except Exception as exc:  # pragma: no cover - DB failure fallback
            self.rollback()
            logger.error(f"保存消息失败: {exc}")
            raise RepositoryError("保存消息失败", details=str(exc)) from exc

    def get_history(self, session_id: str, limit: int = 5) -> List[Message]:
        """Return recent messages for a session ordered by created time."""
        try:
            records = self._db.get_session_messages(session_id, limit)
        except Exception as exc:  # pragma: no cover - DB failure fallback
            logger.error(f"获取聊天历史失败: {exc}")
            raise RepositoryError("获取聊天历史失败", details=str(exc)) from exc

        return [self._to_message(item) for item in records]

    def rollback(self):
        """Rollback current DB transaction if possible."""
        try:
            conn = getattr(self._db, "connection", None)
            if conn:
                conn.rollback()
        except Exception as exc:  # pragma: no cover - defensive
            logger.warning(f"消息回滚失败: {exc}")

    def _to_message(self, item: Dict[str, Any]) -> Message:
        """Map DB row to domain message."""
        metadata = item.get("metadata") if isinstance(item, dict) else {}
        if isinstance(metadata, str):
            metadata = {}

        role_value = item.get("role") if isinstance(item, dict) else None
        if role_value not in {Role.SYSTEM.value, Role.USER.value, Role.ASSISTANT.value}:
            role_value = Role.USER.value

        created_at = None
        if isinstance(item, dict):
            created_at = item.get("created_at") or item.get("timestamp")

        message_id = None
        if isinstance(item, dict):
            message_id = item.get("message_id") or metadata.get("message_id")

        return Message(
            role=Role(role_value),
            content=item.get("content", "") if isinstance(item, dict) else "",
            metadata=metadata or {},
            created_at=created_at,
            message_id=message_id,
        )


# Shared repository instances
session_repository = SessionRepository()
message_repository = MessageRepository()
